package library.utils;

import java.io.BufferedInputStream;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
import library.model.DownloadFilePojo;
import org.apache.commons.io.IOUtils;

/**
 * @author Dmitry
 */
public class FileDownloader extends Thread {

    private DownloadFilePojo book;
    private int contentLength;
    private int currentLength;

    public FileDownloader(DownloadFilePojo book) {
        this.book = book;
    }

    private void updateStatus() {
        int percent = (int) (((float) currentLength / (float) contentLength) * 100);
        book.setStatus(percent);
    }

    private ByteArrayInputStream download(String path) {
        try {
            System.setProperty("http.agent", "Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.4; en-US; rv:1.9.2.2) Gecko/20100316 Firefox/3.6.2");
            URL url = new URL(path);
            URLConnection connection = url.openConnection();
            book.setMessage("Соединение");
            connection.connect();
            HttpURLConnection httpConnection = (HttpURLConnection) connection;
            if (httpConnection.getResponseCode() != 200) {
                throw new RuntimeException("нет fb2");
            }

            if (httpConnection.getContentType().contains("html")) {
                throw new BookUnavailableException("Эта книга недоступна");
            }

            contentLength = httpConnection.getContentLength();
            if (contentLength == -1) {
                Utils.log("Файл с нулевой длиной");
            }
            InputStream stream = httpConnection.getInputStream();
            ByteArrayOutputStream out;
            try (BufferedInputStream bufferedStream = new BufferedInputStream(stream)) {
                book.setMessage("Закачка");
                book.setError(false);
                out = new ByteArrayOutputStream();
                byte[] buf = new byte[1024];
                int n = 0;
                int index = 0;
                while (-1 != (n = bufferedStream.read(buf))) {
                    boolean interupted = isInterrupted();
                    if (interupted) {
                        return null;
                    }

                    out.write(buf, 0, n);
                    currentLength = out.size();
                    if ((index % 10) == 0) {
                        updateStatus();
                    }
                    index++;
                }
                out.close();
            } catch (IOException ex) {
                Utils.log(ex);
                return null;
            }
            updateStatus();
            byte[] response = out.toByteArray();
            ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(response);
            return byteArrayInputStream;
        } catch (MalformedURLException ex) {
            Utils.log(ex);
            return null;
        } catch (IOException ex) {
            Utils.log(ex);
            throw new NetworkErrorException("Ошибка закачки");
        }
    }

    private String formatUrl(int bookId) {
        return "http://flibusta.net/b/" + bookId + "/fb2";
    }

    private void processBook() {
        ByteArrayInputStream byteArrayStream = download(formatUrl(book.getId()));
        if (byteArrayStream == null) {
            return;
        }
        unZipBook(byteArrayStream);
        onEndDownloading();
    }

    private String createPathFromAuthorName(String author) {
        StringBuilder sb = new StringBuilder();
        for (char c : author.toCharArray()) {
            if (c == ' ' || c=='-'||(Character.isJavaIdentifierPart(c))) {
                sb.append(c);
            }
        }
        return sb.toString().trim();
    }

    private void unZipBook(ByteArrayInputStream stream) {
        try {
            try {
                ZipInputStream zipStream = new ZipInputStream(stream);
                ZipEntry entry = zipStream.getNextEntry();
                byte[] buf = new byte[1024];

                while (entry != null) {
                    int n;
                    String entryName = entry.getName();
                    System.out.println("entryname " + entryName);
                    FileOutputStream fileoutputstream;
                    String folder=Utils.getStorePath() + createPathFromAuthorName(book.getAuthor());
                    File dir = new File(folder);
                    dir.mkdirs();

                    fileoutputstream = new FileOutputStream(
                             folder+ "/" + entryName);

                    while ((n = zipStream.read(buf, 0, 1024)) > -1) {
                        fileoutputstream.write(buf, 0, n);
                    }

                    zipStream.closeEntry();
                    entry = zipStream.getNextEntry();
                }
            } catch (IOException ex) {
                Logger.getLogger(FileDownloader.class.getName()).log(Level.SEVERE, null, ex);
            }
            IOUtils.copy(stream, new FileOutputStream("D:/temp.zip"));
        } catch (IOException ex) {
            Logger.getLogger(FileDownloader.class.getName()).log(Level.SEVERE, null, ex);
        }
    }

    @Override
    public void run() {
        while (true) {
            try {
                processBook();
                return;
            } catch (NetworkErrorException ex) {
                book.setMessage("Ошибка. Повтор.");
                book.setError(true);
                continue;
            } catch (BookUnavailableException ex) {
                book.setMessage(ex.getMessage());
                book.setError(true);
                break;
            } catch (RuntimeException ex) {
                book.setMessage(ex.getMessage());
                book.setError(true);
                break;
            }
        }
    }

    private void onEndDownloading() {
        book.setError(false);
        book.setMessage("Закачано");
    }

    private class NetworkErrorException extends RuntimeException {

        public NetworkErrorException(String message) {
            super(message);
        }
    }

    private class BookUnavailableException extends RuntimeException {

        public BookUnavailableException(String message) {
            super(message);
        }
    }
}
